var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var taskManager = require('../common/manager/taskManager');
var pomelo = require('../pomelo');
var rsa = require("node-bignumber");
var events = require('../util/events');
var utils = require('../util/utils');

module.exports = function(app, opts) {
  return new Component(app, opts);
};

/**
 * Connector component. Receive client requests and attach session with socket.
 * 连接器组件            接收客户端请求并且绑定socket到会话
 *
 * @param {Object} app  current application context
 * @param {Object} opts attach parameters
 *                      opts.connector {Object} provides low level network and protocol details implementation between server and clients.
 */
var Component = function(app, opts) {
  opts = opts || {};//参数选项
  this.app = app;//上下文
  this.connector = getConnector(app, opts);//具体使用的connector
  this.encode = opts.encode;//编码    配置中传入的编码函数
  this.decode = opts.decode;//解码    配置中传入的解码函数
  this.useCrypto = opts.useCrypto;//使用加密
  this.blacklistFun = opts.blacklistFun;//黑名单函数
  this.keys = {};//存储RSA公钥
  this.blacklist = [];//黑名单

  if(opts.useDict) {
    app.load(pomelo.dictionary, app.get('dictionaryConfig'));//如果使用路由压缩，加载dict组件
  }

  if(opts.useProtobuf) {
    app.load(pomelo.protobuf, app.get('protobufConfig'));//如果使用protobuf，加载protobuf组件
  }

  // component dependencies
  this.server = null;
  this.session = null;
  this.connection = null;
};

var pro = Component.prototype;

pro.name = '__connector__';

pro.start = function(cb) {
  this.server = this.app.components.__server__;//保存服务器组件引用
  this.session = this.app.components.__session__;//保存会话组件引用
  this.connection = this.app.components.__connection__;//保存连接组件引用

  // check component dependencies//判断依赖的组件是否已经加载
  if(!this.server) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to start connector component for no server component loaded'));
    });
    return;
  }

  if(!this.session) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to start connector component for no session component loaded'));
    });
    return;
  }

  process.nextTick(cb);
};

pro.afterStart = function(cb) {
  this.connector.start(cb);//启动具体的连接器
  this.connector.on('connection', hostFilter.bind(this, bindEvents));//监听connection事件  IP过滤 然后绑定事件
};

pro.stop = function(force, cb) {
  if(this.connector) {
    this.connector.stop(force, cb);
    this.connector = null;
    return;
  }

  process.nextTick(cb);
};

pro.send = function(reqId, route, msg, recvs, opts, cb) {
  logger.debug('[%s] send message reqId: %s, route: %s, msg: %j, receivers: %j, opts: %j', this.app.serverId, reqId, route, msg, recvs, opts);
  var emsg = msg;
  if(this.encode) {
    // use costumized encode
    emsg = this.encode.call(this, reqId, route, msg);
  } else if(this.connector.encode) {
    // use connector default encode
    emsg = this.connector.encode(reqId, route, msg);
  }

  if(!emsg) {
    process.nextTick(function() {
      utils.invokeCallback(cb, new Error('fail to send message for encode result is empty.'));
      return;
    });
  }

  this.app.components.__pushScheduler__.schedule(reqId, route, emsg,
      recvs, opts, cb);
};

pro.setPubKey = function(id, key) {//设置公钥
  var pubKey = new rsa.Key();//新建一个钥匙
  pubKey.n = new rsa.BigInteger(key.rsa_n, 16);//设置n    因为传来的大数N是字符串，而pubKey.n需要是整数，所以需要创建一个新的大数    这个数很大，只能字符串传咯
  pubKey.e = key.rsa_e;//设置e    现在你有了公钥
  this.keys[id] = pubKey;//根据socket id 保存会话钥匙(公钥)
};

pro.getPubKey = function(id) {
  return this.keys[id];
};

var getConnector = function(app, opts) {//获取具体的连接器
    var connector = opts.connector;//首先获取配置的connector，比如connector : pomelo.connectors.hybridconnector,
    //在pomelo.js中：
    //Pomelo.connectors.__defineGetter__('hybridconnector', load.bind(null, './connectors/hybridconnector'));
    //其中返回的是一个工厂函数
  if(!connector) {//如果不存在，使用默认的连接器
    return getDefaultConnector(app, opts);
  }

  if(typeof connector !== 'function') {//如果不是函数，就是实例对象咯?直接返回吧
    return connector;
  }

  var curServer = app.getCurServer();//获取当前服务器信息
  return connector(curServer.clientPort, curServer.host, opts);//创建连接器实例对象
};

var getDefaultConnector = function(app, opts) {//获取默认的连接器对象
  var DefaultConnector = require('../connectors/sioconnector');//默认的为SIO
  var curServer = app.getCurServer();
  return new DefaultConnector(curServer.clientPort, curServer.host, opts);//其实这里不new也可以
};

var hostFilter = function(cb, socket) {//主机IP过滤 这里的socket是高层抽象 如hybridconnector.js
  var ip = socket.remoteAddress.ip;//取得客户端的IP地址
  var check = function(list) {//定义检测函数  list是什么
    for(var address in list) {
      var exp = new RegExp(list[address]);//作为[]存取时属性可以不遵循JS标识符规则
      if(exp.test(ip)) {
        socket.disconnect();//中断连接
        return true;
      }
    }
    return false;
  };
  // dynamical check    动态检查   其中的blacklist通过pomelo-cli进行添加
  if(this.blacklist.length !== 0 && !!check(this.blacklist)) {
    return;
  }
  // static check   静态检查
  if(!!this.blacklistFun && typeof this.blacklistFun === 'function') {//如果黑名单函数存在并且确实是个函数的话
    var self = this;
    self.blacklistFun(function(err, list) {
      if(!!err) {
        logger.error('connector blacklist error: %j', err.stack);
        utils.invokeCallback(cb, self, socket);//如果存在错误，记录错误，但是不影响连接继续
        return;
      }
      if(!Array.isArray(list)) {//判断回传的list是否是数组
        logger.error('connector blacklist is not array: %j', list);
        utils.invokeCallback(cb, self, socket);//回传的不是数组，记录错误，但不影响连接继续
        return;
      }
      if(!!check(list)) {//如果检查命中了，即连接IP存在于黑名单 停止连接过程
        return;
      } else {
        utils.invokeCallback(cb, self, socket);
        return;
      }
    });
  } else {
    utils.invokeCallback(cb, this, socket);//检查通过
  }
};

var bindEvents = function(self, socket) {//绑定事件
  if(self.connection) {//如果连接组件存在
    self.connection.increaseConnectionCount();//增加连接计数
    var statisticInfo = self.connection.getStatisticsInfo();
    var curServer = self.app.getCurServer();//获取当前服务器信息
    if(statisticInfo.totalConnCount > curServer['max-connections']) {//检查当前连接数是否超过了服务器信息中配置的最大连接数
      logger.warn('the server %s has reached the max connections %s', curServer.id, curServer['max-connections']);
      socket.disconnect();//断开后续的连接
      return;
    }
  }

  //create session for connection   //为连接创建新的会话
  var session = getSession(self, socket);
  var closed = false;//闭包数据

  socket.on('disconnect', function() {
    if(closed) {//如果已经关闭
      return;//返回
    }
    closed = true;//更改状态
    if(self.connection) {//如果存在连接组件
      self.connection.decreaseConnectionCount(session.uid);//减少连接计数器
    }
  });

  socket.on('error', function() {//出错了同disconnect处理
    if(closed) {
      return;
    }
    closed = true;
    if(self.connection) {
      self.connection.decreaseConnectionCount(session.uid);
    }
  });

  // new message
  socket.on('message', function(msg) {  //这里的消息是由高层抽象的socket层 比如hybridsocket.js抛出
    var dmsg = msg;
    if(self.decode) {//自定义协议
      dmsg = self.decode(msg);//使用自定义协议解码
    } else if(self.connector.decode) {
      dmsg = self.connector.decode(msg);//使用官方的协议解码 消息层解码
    }
    if(!dmsg) {
            // discard invalid message
            //丢弃非法消息
      return;
    }

    // use rsa crypto
    if(self.useCrypto) {//如果使用了加密
      var verified = verifyMessage(self, session, dmsg);//验证消息
      if(!verified) {
        logger.error('fail to verify the data received from client.');
        return;
      }
    }

    handleMessage(self, session, dmsg);//处理消息
  }); //on message end
};

/**
 * get session for current connection   //为当前连接获取会话
 */
var getSession = function(self, socket) {
  var app = self.app, sid = socket.id;//当前上下文 socket的ID:即为会话ID啊
  var session = self.session.get(sid);//先尝试直接从会话组件中获取
  if(session) {
    return session;//如果获取成功直接返回
  }

  session = self.session.create(sid, app.getServerId(), socket);//创建一个新的会话
  logger.debug('[%s] getSession session is created with session id: %s', app.getServerId(), sid);
   
  // bind events for session    为会话绑定事件
    socket.on('disconnect', session.closed.bind(session));//连接断开事件
    //当socket连接断开时，调用会话的close方法
    

    socket.on('error', session.closed.bind(session));//连接出错事件
    //出错时，也调用会话的关闭方法

    session.on('closed', onSessionClose.bind(null, app));//会话关闭事件 改事件在session.closed中抛出
    //

  session.on('bind', function(uid) {//会话绑定事件
    logger.debug('session on [%s] bind with uid: %s', self.app.serverId, uid);
    // update connection statistics if necessary
    if(self.connection) {//如果存在连接器组件
      self.connection.addLoginedUser(uid, {//添加已登录用户信息
        loginTime: Date.now(),//登录时间
        uid: uid,//用户ID
        address: socket.remoteAddress.ip + ':' + socket.remoteAddress.port//端口和地址
      });
    }
    self.app.event.emit(events.BIND_SESSION, session);//向app内的event对象发送绑定会话事件 谁在监听这个事件呢？？
  });

  session.on('unbind', function(uid) {//会话解绑事件
    if(self.connection) {//如果存在连接器组件
      self.connection.removeLoginedUser(uid);//移除已登录用户信息
    }
    self.app.event.emit(events.UNBIND_SESSION, session);//向app内的event对象发送会话解绑事件
  });

  return session;//返回会话
};

var onSessionClose = function(app, session, reason) {//上下文 会话（前端会话） 原因
  taskManager.closeQueue(session.id, true);//关闭任务队列
  app.event.emit(events.CLOSE_SESSION, session);//向app内的event对象发送关闭会话事件 谁在监听这个事件呢？？
};

var handleMessage = function(self, session, msg) {//处理消息
  logger.debug('[%s] handleMessage session id: %s, msg: %j', self.app.serverId, session.id, msg);
  var type = checkServerType(msg.route);//求出路由中的服务器类型
  if(!type) {//没有类型
    logger.error('invalid route string. route : %j', msg.route);//非法路由
    return;
  }
  self.server.globalHandle(msg, session.toFrontendSession(), function(err, resp, opts) {//调用server组件处理了
    //处理完了，返回了该发送给客户端的数据
    if(resp && !msg.id) {//如果是个响应的话但是消息ID为空(不存在消息ID的消息是通知，请求的消息是包含消息ID的)
      logger.warn('try to response to a notify: %j', msg.route);
      return;
    }
    if (!msg.id && !resp) return;//消息ID为空并且响应也为空
    if (!resp) resp = {};//响应为空 构造一个响应对象
    if (!!err){//如果存在错误
      resp.code = 500;//设置响应的响应码
    }
    opts = {type: 'response', userOptions: opts || {}};//选项
    // for compatiablity
    opts.isResponse = true;

    self.send(msg.id, msg.route, resp, [session.id], opts,//发送消息到前端
      function() {});
  });
};

/**
 * Get server type form request message.    //从请求信息中获取服务器类型
 */
var checkServerType = function (route) {
  if(!route) {//路由信息为空 错误
    return null;//返回空
  }
  var idx = route.indexOf('.');//找到符号'.'所在为止
  if(idx < 0) {//找不到符号'.' 错误
    return null;
  }
  return route.substring(0, idx);//求出字串 该串代表了服务器的类型
};

var verifyMessage = function (self, session, msg) {
  //数字签名过程  
  //签名过程： 原始数据->HASH散列->DER数据编码->|RSA私钥|加密->字节串到位串的转换->得到签名
  //验证签名过程如下
  //           1：签名->位串到字节串的转换->|RSA公钥|解密->DER数据编码->HASH散列
  //           2：拿原始数据做散列，然后比较步骤1产生的散列值 看是否相当，相等则验证成功，不相等则验证失败         
  var sig = msg.body.__crypto__;//首先取出签名结果 
  if(!sig) {
    logger.error('receive data from client has no signature [%s]', self.app.serverId);
    return false;
  }

  var pubKey;//公钥
  
  if(!session) {//验证会话是否存在
    logger.error('could not find session.');
    return false;
  }

  if(!session.get('pubKey')) {//从会话中获取公钥
    pubKey = self.getPubKey(session.id);
    if(!!pubKey) {
      delete self.keys[session.id];
      session.set('pubKey', pubKey);
    }
    else {
      logger.error('could not get public key, session id is %s', session.id);
      return false;
    }
  }
  else {
    pubKey = session.get('pubKey');
  }

  if(!pubKey.n || !pubKey.e) {
    logger.error('could not verify message without public key [%s]', self.app.serverId);
    return false;
  }

  delete  msg.body.__crypto__;

  var message = JSON.stringify(msg.body);
  if(utils.hasChineseChar(message))
    message = utils.unicodeToUtf8(JSON.stringify(msg.body));

  return pubKey.verifyString(message, sig);
};
